package com.peas.platform.common.core.component;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.peas.platform.common.core.configuration.RequestContext;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.configuration.RequestContextUtilEnum;
import com.peas.platform.common.core.dto.ResponseDto;
import com.peas.platform.common.core.feign.PeasModuleSysCoreFeign;
import com.peas.platform.common.core.interceptor.BaseInterceptor;
import com.peas.platform.common.core.utils.LogUtil;
import com.peas.platform.common.core.utils.RequestUtil;
import com.peas.platform.common.core.utils.ResponseUtil;
import com.peas.platform.common.core.utils.SystemTool;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class InterceptorVisit implements BaseInterceptor {

    @Value("${spring.profiles.active:local}")
    private String profiles;

    @Resource
    private ApplicationContext ac;
    @Resource
    private SystemTool systemTool;

    @Override
    public Integer sort() {
        return 1;
    }

    @Override
    public void preHandle(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (StrUtil.startWith(url, "/api")) {
            request.setAttribute("start_timestamp_millis", System.currentTimeMillis());
            RequestContextUtil.getCurrentContext().put(RequestContextUtilEnum.init_timestamp_millis, String.valueOf(System.currentTimeMillis()));
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (StrUtil.startWith(url, "/api")) {
            request.setAttribute("end_timestamp_millis", System.currentTimeMillis());
            RequestContextUtil.getCurrentContext().put(RequestContextUtilEnum.init_timestamp_millis, String.valueOf(System.currentTimeMillis()));
        }
    }

    @SneakyThrows
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response) {
        final String url = request.getRequestURI().toLowerCase();
        if (url.startsWith("/api") && request.getAttribute("start_timestamp_millis") != null && request.getAttribute("end_timestamp_millis") != null) {
            Long start_timestamp_millis = (Long) request.getAttribute("start_timestamp_millis");
            Long end_timestamp_millis = (Long) request.getAttribute("end_timestamp_millis");
            final long duration = end_timestamp_millis - start_timestamp_millis;
            LogUtil.log(this.getClass(), "afterCompletion", request.getRequestURI(), duration, "Interceptor");
            if (StrUtil.equals("local", profiles)) {
                RequestUtil.print(request);
                ResponseUtil.print(response);
            }
            HandlerMethod handlerMethod = null;
            Map<String, RequestMappingHandlerMapping> map = ac.getBeansOfType(RequestMappingHandlerMapping.class);
            if (CollUtil.isNotEmpty(map) && CollUtil.isNotEmpty(map.values())) {
                for (RequestMappingHandlerMapping mapping : map.values()) {
                    HandlerExecutionChain handlerChain = mapping.getHandler(request);
                    if (handlerChain != null) {
                        handlerMethod = (HandlerMethod) handlerChain.getHandler();
                    }
                }
            }
            if (handlerMethod != null) {
                final HttpServletRequest req = request;
                final HttpServletResponse resp = response;
                final HandlerMethod hm = handlerMethod;
                log.debug("1.traceId:{},userId:{}", RequestContextUtil.getCurrentRequestId(), RequestContextUtil.getCurrentUserId());
                if (!StrUtil.equals("local", profiles)) {
                    final RequestContext rc = RequestContextUtil.getRequestContext();
                    new Thread(() -> {
                        RequestUtil.initRequestId(rc);
                        log.debug("2.traceId:{},userId:{}", RequestContextUtil.getCurrentRequestId(), RequestContextUtil.getCurrentUserId());
                        InterceptorVisit.this.visit(rc.getTraceId(), rc.getUserId(), url, hm.getBeanType(), hm.getMethod(), duration, req, resp);
                    }).start();
                }
            }
        }
    }

    @SneakyThrows
    private void visit(String traceId,
                       String userId,
                       String url,
                       Class<?> c,
                       Method m,
                       Long duration,
                       HttpServletRequest req,
                       HttpServletResponse resp) {
        if (systemTool.special()) {
            LogUtil.log(this.getClass(), "visit", "traceId:" + traceId + ",userId:" + userId + ",url" + url + ",c:" + c.getName() + ",m:" + m.getName(), duration);
            return;
        }
        JSONObject data = systemTool.buildJSONObject();
        //接口描述
        ApiOperation ao = m.getAnnotation(ApiOperation.class);
        if (ao != null) {
            data.set("interfaceDesc", ao.value());
        }
        //接口地址
        data.set("interfaceUrl", url);
        //处理类
        data.set("clazz", c.getName());
        //处理方法
        data.set("method", m.getName());
        //请求ContentType
        data.set("reqContentType", req.getContentType());
        //请求Protocol
        data.set("reqProtocol", req.getProtocol());
        //请求Schema
        data.set("reqScheme", req.getScheme());
        //请求PathInfo
        data.set("reqPathInfo", req.getPathInfo());
        //请求PathTranslated
        data.set("reqPathTranslated", req.getPathTranslated());
        //请求URI
        data.set("reqRequestUri", req.getRequestURI());
        //请求URL
        data.set("reqRequestUrl", req.getRequestURL().toString());
        //请求ContextPath
        data.set("reqContextPath", req.getContextPath());
        //请求QueryString
        data.set("reqQueryString", StrUtil.sub(req.getQueryString(), 0, 2000));
        //请求Method
        data.set("reqMethod", req.getMethod());
        //请求AuthType
        data.set("reqAuthType", req.getAuthType());
        //请求ServletPath
        data.set("reqServletPath", req.getServletPath());
        //请求SessionId
        data.set("reqRequestedSessionId", req.getRequestedSessionId());
        //请求RemoteUser
        data.set("reqRemoteUser", req.getRemoteUser());
        //请求RemoteAddr
        data.set("reqRemoteAddr", req.getRemoteAddr());
        //请求RemoteHost
        data.set("reqRemoteHost", req.getRemoteHost());
        //请求RemotePort
        data.set("reqRemotePort", String.valueOf(req.getRemotePort()));
        //请求ServerName
        data.set("reqServerName", req.getServerName());
        //请求ServerPort
        data.set("reqServerPort", String.valueOf(req.getServerPort()));
        //请求LocalAddr
        data.set("reqLocalAddr", req.getLocalAddr());
        //请求LocalName
        data.set("reqLocalName", req.getLocalName());
        //请求LocalPort
        data.set("reqLocalPort", String.valueOf(req.getLocalPort()));
        //请求Cookies
        Map<String, String> cookie = new HashMap<>();
        Cookie[] cookies = req.getCookies();
        if (cookies != null) {
            for (Cookie coo : cookies) {
                cookie.put(coo.getName(), coo.getValue());
            }
        }
        data.set("reqCookies", JSONUtil.toJsonStr(cookie));
        //请求ParameterMap
        data.set("reqParameterMap", JSONUtil.toJsonStr(req.getParameterMap()));
        //请求TrailerFields
        data.set("reqTrailerFields", JSONUtil.toJsonStr(req.getTrailerFields()));
        //请求Headers
        Map<String, String> header1 = new HashMap<>();
        Enumeration<String> headers = req.getHeaderNames();
        while (headers != null && headers.hasMoreElements()) {
            String name = headers.nextElement();
            header1.put(name, req.getHeader(name));
        }
        data.set("reqHeaders", JSONUtil.toJsonStr(header1));
        //响应Status
        data.set("respStatus", String.valueOf(resp.getStatus()));
        //响应CharacterEncoding
        data.set("respCharacterEncoding", resp.getCharacterEncoding());
        //响应ContentType
        data.set("respContentType", resp.getContentType());
        //响应Headers
        Map<String, String> header2 = new HashMap<>();
        if (CollUtil.isNotEmpty(header2)) {
            for (String name : resp.getHeaderNames()) {
                header2.put(name, resp.getHeader(name));
            }
        }
        data.set("respHeader", JSONUtil.toJsonStr(header2));
        //访问请求ID
        data.set("visitRequestId", traceId);
        //访问时间
        data.set("visitTime", DateUtil.formatDateTime(DateUtil.date()));
        //访问用户ID
        data.set("visitUser", userId);
        //访问耗时(毫秒)
        data.set("visitDuration", String.valueOf(duration));
        log.debug("call peasModuleSysMdmFeign.addVisit:{}", JSONUtil.toJsonStr(data));
        PeasModuleSysCoreFeign peasModuleSysCoreFeign = ac.getBean(PeasModuleSysCoreFeign.class);
        ResponseDto<Integer> result = peasModuleSysCoreFeign.addVisit(data);
        log.debug("call peasModuleSysMdmFeign.addVisit, result:{}", result);
    }

}
